package com.broker.upload;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

@RestController
@RequestMapping(value = "/file")
public class UploadController {

    private String fileDir = "/Users/xuyan/Desktop/upload";
    private List<Integer> chunkList = new ArrayList<>();
    /**
     * 当分片下载时：
     * 参数：chunk,chunks,type,name,uid,id,lastModifiedDate,size,md5,file
     * 描述：md5值由upload框架生成，是根据文件指定字节数的内容生成；
     * 存储md5为文件的唯一标识，存储下载保存完成之后的所有chunk，保存的chunk值可能不是按照上传顺序保存的。
     * 所有分片下载完成之后，先对文件集合进行排序，然后再按顺序拼接文件流，然后再删除分片文件。
     * 不分片下载时：
     * 参数：type,name,uid,id,lastModifiedDate,size,md5,file
     * 描述：需要设置可接受的最大文件数目。
     *
     * @param file
     * @param request  upload框架自带的参数：chunk,chunks,type,name,uid,id,lastModifiedDate,size,file 我自定义的参数:md5
     * @param response
     * @return
     * @throws IOException
     * @throws ServletException
     */
    @RequestMapping(value = "/upload")
    public  String upload(MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

        int chunk = 0, chunks = 0;
        long fileSize = 0;
        String chunk_param = request.getParameter("chunk");
        String chunks_param = request.getParameter("chunks");
        String type_param = request.getParameter("type");
        String name_param = request.getParameter("name");
        String uid_param = request.getParameter("uid");
        String id_param = request.getParameter("id");
        String lastModifiedDate_param = request.getParameter("lastModifiedDate");
        String size_param = request.getParameter("size");
        String md5_param = request.getParameter("md5");
        ServletInputStream inputStream = request.getInputStream();
        if (chunk_param != null) {
            chunk = Integer.valueOf(chunk_param);
        }
        if (chunks_param != null) {
            chunks = Integer.valueOf(chunks_param);
        }
        if (size_param != null) {
            fileSize = Long.valueOf(size_param);
        }
        chunkList.add(chunk);
        System.out.println(chunkList.size()+"------"+chunk);
//        System.out.println(chunks_param + "-----" + chunk_param);

        File uploadDir = new File(fileDir + File.separator + md5_param);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }

        String partFileName = "";
        //chunk为null代表未分片上传，否则是分片上传
        //分片上传片段暂时设置无扩展名
        if (chunk_param == null) {
            //有扩展名的完整文件名
            String type = name_param.substring(name_param.lastIndexOf(".") + 1, name_param.length());
            partFileName = uploadDir.getPath() + File.separator + md5_param + "." + type;
        } else {
            //无扩展名的片段文件名称
            partFileName = uploadDir.getPath() + File.separator + md5_param + "-" + chunk_param;
        }
        File partFile = new File(partFileName);
//        if (partFile.exists()&&partFile.isFile()){
//            partFile.delete();
//        }
        if (file == null) {
            FileCopyUtils.copy(request.getInputStream(), new FileOutputStream(partFile));
        } else {
            file.transferTo(partFile);
        }
        //判断是否下载完成
        //校验md5所对应的文件的chunk下载完整性，和状态
        uploadPro(md5_param, chunk, chunks);
//        //分片下载完成
//        if (finish&&chunk_param!=null) {
//            File[] files = uploadDir.listFiles(new FilenameFilter() {
//                @Override
//                public boolean accept(File dir, String name) {
//                    if (name.startsWith(md5_param))
//                        return true;
//                    return false;
//                }
//            });
//            if (files.length >= chunks) {
//                String type = name_param.substring(name_param.lastIndexOf(".")+1,name_param.length());
//                File resultFile = new File(uploadDir.getPath()+File.separator+md5_param+"."+type);
//                compFile(files,resultFile,chunks,md5_param);
//            }
//        }

        return name_param;
    }

    String fileType = "png#jpeg#jpg#pdf#rar#zip#7zip";

    /**
     * 文件校验
     *
     * @param md5
     * @param type
     * @return
     */
    @RequestMapping(value = "/verify")
    public String verify(@RequestParam("md5") String md5, @RequestParam("type") String type,@RequestParam("chunks") String chunks) {
        writeLog(md5 + "\n");
        if (!fileType.contains(type)) {
            return "{\"code\":\"-1\",\"msg\":\"文件类型不符合要求\"}";
        }
        if (md5 == null || md5.isEmpty()) {
            return "{\"code\":\"-2\",\"msg\":\"文件错误\"}";
        }
        String verifyMD5 = readPro(md5);
        if (verifyMD5 == null) {
            return "{\"code\":\"0000\",\"msg\":\"文件存在继续下载\",\"chunk\":" + verifyMD5 + "}";
        }
        String[] value = verifyMD5.split("#");
        int chunks_param = 0;
        if (chunks!=null){
            chunks_param = Integer.valueOf(chunks);
        }
        //文件已经上传过了
        if (value.length -1 >= chunks_param){
            File uploadFile = new File(fileDir + File.separator + md5 + File.separator + md5+"."+type);
            if (uploadFile.exists()&&uploadFile.isFile()){
                return "{\"code\":\"0001\",\"msg\":\"文件已经下载完成\",\"chunk\":0}";
            }
        }
        return "{\"code\":\"0000\",\"msg\":\"文件验证通过\",\"chunk\":0}";
    }


    /**
     * 片段校验
     *
     * @param md5
     * @param chunk
     * @return
     */
    @RequestMapping(value = "/getVerifyChunk")
    public String getVerifyChunk(@RequestParam("md5") String md5, @RequestParam("chunk") String chunk) {
        if (md5 == null || md5.isEmpty()) {
            return "{\"code\":\"-2\",\"msg\":\"文件错误\"}";
        }
        String chunks = readPro(md5);
        if (chunks == null) {
            return "{\"code\":\"0000\",\"msg\":\"文件存在继续下载\",\"isSkip\":0}";
        }
        if (chunks.contains("#" + chunk + "#")) {
            File uploadFile = new File(fileDir + File.separator + md5 + File.separator + md5+"-"+chunk);
            if (uploadFile.exists()&&uploadFile.isFile()) {
                return "{\"code\":\"0000\",\"msg\":\"文件验证通过\",\"isSkip\":1}";
            }
        }
        return "{\"code\":\"0000\",\"msg\":\"文件验证通过\",\"isSkip\":0}";
    }

    /**
     * 文件片段完整性校验，并合并
     *
     * @param md5
     * @param type   文件类型
     * @param chunks
     * @return
     */
    @RequestMapping(value = "/mergeFile")
    public String mergeFile(@RequestParam("md5") String md5, @RequestParam("type") String type, @RequestParam("chunks") String chunks) {
        //分片下载完成
        System.out.println("mergeFile-"+md5+"-"+type+"-"+chunks);
        int chunks_params = 0;
        if (chunks != null) {
            chunks_params = Integer.valueOf(chunks);
        }
        if (chunks_params > 1) {
            File uploadDir = new File(fileDir + File.separator + md5);
            File[] files = uploadDir.listFiles(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    if (name.startsWith(md5))
                        return true;
                    return false;
                }
            });
            if (files == null){
                return "{\"code\":\"-1\",\"msg\":\"片段文件不存在\",\"chunk\":0}";
            }
            if (files.length >= chunks_params) {
                File resultFile = new File(uploadDir.getPath() + File.separator + md5 + "." + type);
                compFile(files, resultFile, chunks_params, md5);
            }else{
                return "{\"code\":\"-1\",\"msg\":\"缺少片段文件\",\"chunk\":0}";
            }
        }
        return "{\"code\":\"0000\",\"msg\":\"文件验证通过\",\"chunk\":0}";
    }


    public void writeLog(String log) {
        File file = new File("log.txt");
        try {
            FileOutputStream fos = new FileOutputStream(file, true);
            fos.write(log.getBytes("utf-8"));
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private synchronized boolean uploadPro(String md5, int chunk, int chunks) {
        String chunkValue = readPro(md5);
        System.out.println(chunkValue);
        String chunksValue = setChunk(chunkValue, "" + chunk);
        System.out.println(chunksValue);
        addPro(md5, chunksValue);
        String[] chunkArr = chunksValue.split("#");
        System.out.println("chunklength:" + chunkArr.length + "");
        if (chunkArr.length - 1 == chunks) {
            return true;
        }
        return false;
    }


    private String readPro(String key) {
        Properties properties = new Properties();
        String value = "";
        // 使用InPutStream流读取properties文件
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new FileReader("fileverify.properties"));
            properties.load(bufferedReader);
            // 获取key对应的value值
            value = properties.getProperty(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return value;
    }

    private void addPro(String key, String value) {
        Properties properties = new Properties();
        // 使用InPutStream流读取properties文件
        String propertiesName = "fileverify.properties";
        FileOutputStream outputStream = null;
        BufferedReader bufferedReader = null;
        try {
            // 获取key对应的value值
            bufferedReader = new BufferedReader(new FileReader(propertiesName));
            properties.load(bufferedReader);
            properties.setProperty(key, value);
            outputStream = new FileOutputStream(new File(propertiesName));
            properties.store(outputStream, null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                if (bufferedReader!=null){
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private String setChunk(String chunk_pro, String chunk) {
        String _chunk = chunk + "#";
        if (chunk_pro == null || chunk_pro.isEmpty()) {
            _chunk = "#" + _chunk;
            return _chunk;
        }
        return chunk_pro + _chunk;
    }

    private synchronized boolean compFile(File[] files, File file, int chunks, String md5) {
        System.out.println(file.getPath());
        try {
            Vector<FileInputStream> streamList = new Vector<>();
            for (int i = 0; i < chunks; i++) {
                for (File f : files) {
                    if (f.getName().equals(md5 + "-" + i)) {
                        streamList.add(new FileInputStream(f));
                    }
                }
            }
            SequenceInputStream allStream = new SequenceInputStream(streamList.elements());
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file, true));
            byte bs[] = new byte[1024];
            int len = -1;
            //先读第一个
            while ((len = allStream.read(bs)) != -1) {
                out.write(bs, 0, len);
                out.flush();
            }
            allStream.close();
            out.close();
            //拼接成功之后删除分片
            for (File delete : files) {
                if (delete.exists() && delete.isFile()) {
                    delete.delete();
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }

}
